import { createElement } from 'react';
import { FormPath } from '@formily/core';
import { toJS } from '@formily/reactive';
import {
  ArrayField,
  Field as InternalField,
  ObjectField,
  VoidField,
  observer,
  ISchema,
  Schema,
} from '@formily/react';
import { FormItem as Item } from '@formily/antd';
import { each, reduce } from '@formily/shared';
import { createBehavior } from '@inbiz/core';
import {
  useDesigner,
  useTreeNode,
  useComponents,
  DnFC,
  getMessage,
} from '@inbiz/react';
import { useInbiz } from '@inbiz/render';
import { isArr, isStr } from '@inbiz/shared';

import { Container } from '../Container';
import { Field as FieldLocales } from './locale';
import * as Decorators from '../Decorators';
import { ErrorBoundary, getPlatform } from '@inbiz/utils';


Schema.silent(true);
const SchemaStateMap = {
  title: 'title',
  description: 'description',
  default: 'value',
  enum: 'dataSource',
  readOnly: 'readOnly',
  writeOnly: 'editable',
  required: 'required',
  'x-content': 'content',
  'x-value': 'value',
  'x-editable': 'editable',
  'x-disabled': 'disabled',
  'x-read-pretty': 'readPretty',
  'x-read-only': 'readOnly',
  'x-visible': 'visible',
  'x-hidden': 'hidden',
  'x-display': 'display',
  'x-pattern': 'pattern',
};

const NeedShownExpression = {
  title: true,
  description: true,
  default: true,
  'x-content': true,
  'x-value': true,
};

const isExpression = (val: any) => isStr(val) && /^\{\{.*\}\}$/.test(val);

const filterExpression = (val: any) => {
  if (typeof val === 'object') {
    const isArray = isArr(val);
    const results = reduce(
      val,
      (buf: any, value, key) => {
        if (isExpression(value)) {
          return buf;
        } else {
          const results = filterExpression(value);
          if (results === undefined || results === null) return buf;
          if (isArray) {
            return buf.concat([results]);
          }
          buf[key] = results;
          return buf;
        }
      },
      isArray ? [] : {},
    );
    return results;
  }
  if (isExpression(val)) {
    return;
  }
  return val;
};
const FormItem = (props?: { className: string }) =>
  createElement(Item, {
    ...props,
    className: `${props?.className || ''} ${getPlatform()}`,
  });
const toDesignableFieldProps = (
  schema: ISchema,
  components: any,
  nodeIdAttrName: string,
  id: string,
  inbiz: any,
) => {
  const results: any = {};
  each(SchemaStateMap, (fieldKey, schemaKey) => {
    const value = schema[schemaKey];
    if (isExpression(value)) {
      if (!NeedShownExpression[schemaKey]) return;
      if (value) {
        results[fieldKey] = value;
        return;
      }
    } else if (value) {
      results[fieldKey] = filterExpression(value);
    }
  });

  // 将props的默认值转换成value
  // if (schema['x-component-props']?.default !== undefined) {
  //   results['value'] = filterExpression(schema['x-component-props'].default);
  // }
  if (!components['FormItem']) {
    components['FormItem'] = FormItem;
  }

  // 注入绑定Decorators
  Object.keys(Decorators).map((key) => {
    if (!components[key]) {
      components[key] = Decorators[key];
    }
  });
  const decorator =
    schema['x-decorator'] && FormPath.getIn(components, schema['x-decorator']);
  const component =
    schema['x-component'] && FormPath.getIn(components, schema['x-component']);

  const decoratorProps = schema['x-decorator-props'] || {};
  const componentProps = schema['x-component-props'] || {};

  // 统一添加扩展名称
  decoratorProps.className = `${schema?.['x-component']}Container ${schema?.['x-component-props']?.className || ''
    } ${getPlatform()}`;

  // 存在默认值转换器时，将默认值进行转换
  if (decorator) {
    results.decorator = [decorator, decoratorProps];
  }
  if (component) {
    const newComponentProps = {
      ...toJS(componentProps),
      ...schema['x-component-biz-props'],
      title: results.title,
      inbiz,
      controlId: schema.name,
      __componentname: schema['x-component']
    };
    if (component?.defaultValueFormat) {
      const value = component.defaultValueFormat(newComponentProps);
      if (value && typeof value === 'object' && value.then) {
        value.then((res: any) => {
          results['value'] = res;
        });
      } else {
        results['value'] = value;
      }
    }
    results.component = [component, newComponentProps];
  }
  if (decorator) {
    FormPath.setIn(results['decorator'][1], nodeIdAttrName, id);
  } else if (component) {
    FormPath.setIn(results['component'][1], nodeIdAttrName, id);
  }
  // 多语言
  if (results?.title?.value) {
    results.title.value = getMessage(results.title.value);
  }

  if (typeof results?.title === 'object') {
    results.title = results.title?.status ? results.title.value : undefined;
  }
  results.title = results.title && (
    <span data-content-editable="title">{results.title}</span>
  );
  results.description = results.description && (
    <span data-content-editable="description">{results.description}</span>
  );
  if (results?.display === 'hidden') {
    results.component = [];
    results.title = '';
    results.display = 'visible';
  }
  return results;
};

const FieldCore: DnFC<ISchema> = observer((props) => {
  const designer = useDesigner();
  const components = useComponents();
  const node = useTreeNode();
  const inbiz = useInbiz();

  if (!node) return null;
  const fieldProps = toDesignableFieldProps(
    props,
    components,
    designer.props.nodeIdAttrName!,
    node.id,
    inbiz,
  );
  // 添加组件props, 唯一key
  if (props['sourceModelUpdate']) {
    try {
      const sourceData = JSON.parse(props['sourceModelUpdate']);
      fieldProps.primarykey = sourceData?.primaryKey || 'id';
    } catch {
      fieldProps.primarykey = 'id';
    }
  }

  if (props.type === 'object') {
    return (
      <Container>
        <ObjectField {...fieldProps} name={node.id}>
          {props['x-display'] != 'hidden' && props.children}
        </ObjectField>
      </Container>
    );
  } else if (props.type === 'array') {
    const value = fieldProps.value
      ? Array.isArray(fieldProps.value)
        ? fieldProps.value
        : [fieldProps.value]
      : [];
    return <ArrayField {...fieldProps} value={value} name={node.id} />;
  } else if (node.props.type === 'void') {
    return (
      <VoidField {...fieldProps} name={node.id}>
        {props['x-display'] != 'hidden' && props.children}
      </VoidField>
    );
  }
  return <InternalField {...fieldProps} name={node.id} />;
});

export const Field: DnFC<ISchema> = (props) => {
  return <ErrorBoundary>
    <FieldCore  {...props} />
  </ErrorBoundary>
}

Field.Behavior = createBehavior({
  name: 'Field',
  selector: 'Field',
  designerLocales: FieldLocales,
});
